1 Presentación

Bienvenidos a mi entrega del reto de Sports Analytics para el Data Science Awards Spain 2019.

Me llamo Xavier Vivancos, de Barcelona, y soy graduado en Ingeniería de Sistemas Audiovisuales por la Universidad Pompeu Fabra (2016). Posteriormente, viendo el boom del Big Data y la gran cantidad de oportunidades laborales que generaba este ámbito, decidí cambiar el rumbo de mi trayectoria académica y cursé el Máster en Inteligencia de Negocio y Big Data Analytics en la UOC (finalizado en 2018). Fue un movimiento arriesgado, puesto que no sabía con certeza si iba a agradarme o no. Por suerte, a día de hoy, puedo decir que no me equivoqué en absoluto y que me encantaría enfocar mi carrera profesional en la Ciencia de Datos.

Aunque actualmente me dedico a tareas más relacionadas con Business Intelligence, mi deseo es el de poder trabajar como Data Scientist. En concreto, me encantaría adentrarme en el mundo de Sports Analytics. Debida a mi corta experiencia en este ámbito, decidí crearme una cuenta y colaborar regularmente en Kaggle, con el objetivo de crear un portafolio que demuestre mis capacidades (podéis visitar mi perfil aquí). Mis trabajos en la plataforma se basan, sobre todo, en narrar historias a partir del análisis de la información. ¡Me encanta el data storytelling!

2 Motivación

¡…y también me encanta el baloncesto! Llevo jugando en equipo desde bien pequeño y es parte de mi vida. No disfruto sólo como jugador, sino también como espectador (tanto baloncesto europeo como NBA). Además, me gusta estar actualizado acerca de cualquier noticia (fichajes, resultados, polémicas, etc.) que tenga relación con este deporte. Por cierto, ¡una buena parte de mis trabajos en Kaggle son de baloncesto!

Cuando un amigo me comentó que uno de los posibles retos del torneo era de analítica deportiva aplicada al baloncesto, no me lo pensé dos veces y decidí participar.

3 Herramientas

He decidido utilizar el lenguaje de programación R para la elaboración del reto. ¿Por qué? Porque es el principal lenguaje que aprendí y utilicé durante el Máster. También lo uso para mis análisis en Kaggle. Es con el que llevo más tiempo, el que más domino y con el que me siento más cómodo. En concreto, he trabajado con el entorno de trabajo de RStudio, utilizando un archivo R Markdown con el que puedo combinar código y letra, y pudiéndolo exportar a formato HTML para su posterior lectura.

4 Lectura e inspección de datos

Los datos proporcionados son los siguientes:

Antes de entrar en los análisis, vamos a leer e inspeccionar los diferentes archivos, analizando las observaciones, variables y su tipo. En el fichero de los equipos de la ACB voy a añadir tres columnas nuevas para enriquecer el análisis:

# Librerías
library(openxlsx)
library(tidyverse)
library(knitr)
library(magick)
library(grid)
library(plotly)
library(gganimate)
library(gridExtra)
library(factoextra)
library(GGally)
library(caret)
library(C50)
library(corrplot)

# Lectura de datos
ACB_Players_1819 <- read.xlsx("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/ACB_Players_18-19.xlsx", rowNames=TRUE)
ACB_Players_1218 <- read.xlsx("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/ACB_Players_2012to2018.xlsx")
ACB_Teams_1819 <- read.xlsx("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/ACB_Teams_18-19.xlsx", rowNames=TRUE)

# Añadimos una columna nueva: Playoffs 
ACB_Teams_1819$Playoffs <- c("No clasificado", "No clasificado", "Clasificado", "Clasificado",
                             "No clasificado", "No clasificado", "Clasificado", "Clasificado",
                             "No clasificado", "No clasificado", "No clasificado", "Clasificado",
                             "No clasificado", "No clasificado", "Clasificado", "No clasificado",
                             "Clasificado", "Clasificado")

# Añadimos una columna nueva: Puntos recibidos
ACB_Teams_1819$`Puntos recibidos` <- c(84.35, 82.09, 80.38, 76.18, 84.50, 79.71, 82.18, 74.97, 88.85, 
                                       81.91, 87.68, 78.76, 83.03, 83.50, 85.21, 81.91, 81.56, 78.62)

# Añadimos una columna nueva: Posicion 
ACB_Teams_1819$Posicion <- c(18, 17, 7, 2, 12, 9, 8, 3, 13, 
                             10, 16, 1, 15, 11, 6, 14, 5, 4)

4.1 ACB_Players_18-19

# Estructura
glimpse(ACB_Players_1819)
## Observations: 276
## Variables: 46
## $ Player      <chr> "David Jelinek", "Shayne Whittington", "David Walk...
## $ Team        <chr> "AND", "AND", "AND", "AND", "AND", "AND", "AND", "...
## $ Team_full   <chr> "MoraBanc Andorra", "MoraBanc Andorra", "MoraBanc ...
## $ Position    <chr> "SG", "C", "GF", "F", "PG", "F", "G", "G", "FC", "...
## $ altura      <dbl> 196, 211, 198, 198, 178, 201, 196, 188, 208, 213, ...
## $ Peso        <dbl> 86, 113, 91, 98, 75, 101, 89, 88, 100, 115, 92, 99...
## $ Nationality <chr> "Czech Republic", "United States", "United States"...
## $ temporada   <chr> "2018-2019", "2018-2019", "2018-2019", "2018-2019"...
## $ GP          <dbl> 30, 12, 16, 2, 33, 34, 34, 33, 21, 19, 34, 32, 34,...
## $ MPG         <dbl> 19.6, 14.4, 20.1, 5.4, 25.2, 16.9, 20.2, 25.0, 13....
## $ FGM         <dbl> 3.0, 3.6, 3.1, 0.5, 2.8, 2.6, 2.9, 4.5, 2.2, 2.0, ...
## $ FGA         <dbl> 7.8, 6.8, 6.9, 1.5, 7.4, 6.0, 7.0, 10.2, 3.7, 4.3,...
## $ `FG%`       <dbl> 0.380, 0.524, 0.445, 0.333, 0.379, 0.438, 0.414, 0...
## $ `3PM`       <dbl> 1.6, 0.8, 0.9, 0.0, 1.7, 0.5, 1.4, 1.6, 0.0, 0.0, ...
## $ `3PA`       <dbl> 4.2, 2.3, 2.9, 0.5, 4.8, 2.0, 4.0, 4.3, 0.0, 0.0, ...
## $ `3P%`       <dbl> 0.370, 0.357, 0.298, 0.000, 0.348, 0.265, 0.338, 0...
## $ FTM         <dbl> 1.6, 1.7, 1.5, 0.0, 1.1, 0.5, 0.8, 2.9, 1.6, 2.4, ...
## $ FTA         <dbl> 1.9, 2.2, 1.8, 0.0, 1.4, 1.1, 1.4, 3.9, 2.1, 3.6, ...
## $ `FT%`       <dbl> 0.825, 0.741, 0.828, 0.000, 0.761, 0.500, 0.553, 0...
## $ TOV         <dbl> 1.0, 0.8, 0.9, 0.0, 2.0, 0.6, 1.0, 1.7, 0.9, 1.0, ...
## $ PF          <dbl> 2.3, 2.5, 1.4, 1.0, 2.1, 2.0, 1.9, 2.0, 2.2, 1.9, ...
## $ ORB         <dbl> 0.6, 1.8, 0.4, 0.0, 0.4, 1.3, 0.3, 0.8, 1.5, 1.0, ...
## $ DRB         <dbl> 1.9, 2.0, 1.7, 0.5, 1.4, 2.9, 1.9, 2.2, 1.6, 1.9, ...
## $ RPG         <dbl> 2.5, 3.8, 2.1, 0.5, 1.8, 4.1, 2.1, 3.0, 3.1, 2.9, ...
## $ APG         <dbl> 1.3, 0.8, 1.2, 0.5, 5.2, 0.4, 2.0, 2.7, 0.5, 0.7, ...
## $ SPG         <dbl> 0.6, 0.5, 0.5, 0.0, 1.2, 0.5, 0.5, 0.8, 0.6, 0.3, ...
## $ BPG         <dbl> 0.2, 0.6, 0.1, 0.0, 0.0, 0.3, 0.0, 0.2, 0.1, 0.6, ...
## $ PPG         <dbl> 9.1, 9.7, 8.5, 1.0, 8.3, 6.3, 7.9, 13.4, 6.0, 6.4,...
## $ `TS%`       <dbl> 0.525, 0.618, 0.554, 0.333, 0.520, 0.489, 0.520, 0...
## $ `eFG%`      <dbl> 0.481, 0.585, 0.509, 0.333, 0.492, 0.483, 0.511, 0...
## $ `Total.S.%` <dbl> 157.5, 162.2, 157.1, 33.3, 148.8, 120.3, 130.5, 15...
## $ `ORB%`      <dbl> 3.7, 15.0, 2.5, 0.0, 1.9, 9.2, 1.6, 3.6, 13.7, 7.0...
## $ `DRB%`      <dbl> 12.2, 18.9, 10.9, 10.7, 7.1, 21.6, 11.9, 11.3, 16....
## $ `TRB%`      <dbl> 7.8, 16.8, 6.5, 5.3, 4.4, 15.2, 6.6, 7.4, 14.8, 13...
## $ `AST%`      <dbl> 11.8, 11.8, 10.4, 13.1, 34.2, 3.7, 16.9, 19.7, 7.2...
## $ `TOV%`      <dbl> 10.1, 8.7, 10.9, 0.0, 20.0, 8.0, 11.4, 12.2, 16.5,...
## $ `STL%`      <dbl> 1.6, 1.9, 1.4, 0.0, 2.7, 1.7, 1.3, 1.8, 2.5, 0.5, ...
## $ `BLK%`      <dbl> 1.1, 4.4, 0.7, 0.0, 0.1, 2.1, 0.2, 0.8, 0.8, 3.5, ...
## $ `USG%`      <dbl> 22.8, 27.2, 20.1, 12.6, 18.5, 19.4, 19.7, 25.4, 19...
## $ PPR         <dbl> -0.5, -1.7, -0.7, 5.8, 5.8, -1.9, 1.7, 0.4, -4.1, ...
## $ PPS         <dbl> 1.2, 1.4, 1.2, 0.7, 1.1, 1.1, 1.1, 1.3, 1.6, 1.5, ...
## $ ORtg        <dbl> 108.4, 127.7, 110.9, 93.3, 108.9, 104.5, 107.2, 11...
## $ DRtg        <dbl> 113.2, 111.8, 112.5, 116.9, 112.9, 109.1, 114.0, 1...
## $ eDiff       <dbl> -4.8, 15.9, -1.6, -23.6, -4.1, -4.6, -6.8, 0.7, 11...
## $ FIC         <dbl> 134.6, 82.4, 72.9, 0.5, 236.0, 153.0, 148.6, 253.2...
## $ PER         <dbl> 12.8, 27.3, 12.6, 2.4, 12.6, 12.9, 11.1, 16.7, 19....

4.2 ACB_Players_2012to2018

# Estructura
glimpse(ACB_Players_1218)
## Observations: 1,815
## Variables: 46
## $ Player       <chr> "Oliver Stevic", "Tomas Hampl", "Kostas Vasiliadi...
## $ Team         <chr> "BBB", "BBB", "BBB", "BBB", "BBB", "BBB", "BBB", ...
## $ Team_full    <chr> "RETAbet Bilbao Basket", "RETAbet Bilbao Basket",...
## $ Pos          <chr> "FC", "C", "F", "G", "SF", "SF", "C", "PG", "G", ...
## $ `Height(ft)` <chr> "6-10", "7-1", "6-7", "6-2", "6-7", "6-8", "6-10"...
## $ `Weight(lb)` <chr> "220", "240", "225", "200", "220", "250", "265", ...
## $ Nationality  <chr> "Serbia", "Czech Republic", "Greece", "United Sta...
## $ temporada    <chr> "2011-2012", "2011-2012", "2011-2012", "2011-2012...
## $ GP           <dbl> 4, 3, 35, 30, 36, 34, 31, 35, 36, 33, 30, 34, 33,...
## $ MPG          <dbl> 11.6, 8.9, 21.9, 11.1, 26.2, 27.1, 13.8, 15.1, 25...
## $ FGM          <dbl> 1.2, 1.7, 3.3, 1.4, 3.6, 4.7, 1.4, 2.0, 3.9, 2.0,...
## $ FGA          <dbl> 2.8, 3.3, 8.1, 2.7, 9.0, 7.9, 3.0, 4.3, 8.1, 4.7,...
## $ `FG%`        <dbl> 0.455, 0.500, 0.406, 0.512, 0.404, 0.593, 0.457, ...
## $ `3PM`        <dbl> 0.0, 0.0, 1.6, 0.5, 1.2, 0.1, 0.0, 1.2, 0.9, 1.2,...
## $ `3PA`        <dbl> 0.0, 0.0, 5.3, 1.5, 3.8, 0.3, 0.0, 2.7, 2.2, 3.1,...
## $ `3P%`        <dbl> 0.000, 0.000, 0.299, 0.341, 0.304, 0.222, 0.000, ...
## $ FTM          <dbl> 1.0, 1.0, 3.9, 0.1, 1.6, 2.1, 0.9, 1.1, 2.2, 0.5,...
## $ FTA          <dbl> 1.5, 1.3, 4.3, 0.2, 2.1, 2.4, 1.6, 1.3, 2.9, 0.7,...
## $ `FT%`        <dbl> 0.667, 0.750, 0.907, 0.500, 0.727, 0.855, 0.569, ...
## $ TOV          <dbl> 0.8, 0.7, 1.5, 0.6, 2.8, 1.4, 1.1, 0.9, 1.8, 0.9,...
## $ PF           <dbl> 1.8, 1.7, 1.5, 1.1, 2.3, 2.9, 2.1, 1.9, 2.2, 2.5,...
## $ ORB          <dbl> 1.2, 1.3, 0.4, 0.3, 0.8, 1.4, 1.5, 0.2, 0.8, 0.1,...
## $ DRB          <dbl> 1.8, 1.3, 1.9, 1.2, 3.8, 2.9, 1.9, 1.1, 2.6, 1.2,...
## $ RPG          <dbl> 3.0, 2.7, 2.4, 1.5, 4.5, 4.3, 3.4, 1.3, 3.3, 1.2,...
## $ APG          <dbl> 0.8, 0.0, 1.1, 0.7, 2.8, 0.8, 0.3, 1.5, 2.9, 0.6,...
## $ SPG          <dbl> 0.0, 0.0, 0.7, 0.5, 0.7, 0.6, 0.4, 1.0, 0.9, 0.3,...
## $ BPG          <dbl> 0.5, 0.0, 0.2, 0.3, 0.1, 0.3, 0.3, 0.0, 0.0, 0.0,...
## $ PPG          <dbl> 3.5, 4.3, 12.0, 3.4, 10.0, 11.5, 3.6, 6.3, 10.9, ...
## $ `TS%`        <dbl> 0.513, 0.553, 0.603, 0.603, 0.503, 0.642, 0.494, ...
## $ `eFG%`       <dbl> 0.455, 0.500, 0.504, 0.604, 0.469, 0.597, 0.457, ...
## $ `TotalS%`    <dbl> 112.1, 125.0, 161.2, 135.3, 143.6, 167.1, 102.5, ...
## $ `ORB%`       <dbl> 16.3, 20.4, 2.8, 3.9, 4.0, 7.3, 15.2, 1.9, 4.1, 0...
## $ `DRB%`       <dbl> 20.5, 18.5, 11.5, 13.9, 18.5, 13.7, 18.5, 9.8, 12...
## $ `TRB%`       <dbl> 18.5, 19.4, 7.3, 9.1, 11.6, 10.6, 16.9, 6.0, 8.7,...
## $ `AST%`       <dbl> 10.2, 0.0, 9.4, 11.0, 19.6, 5.4, 3.2, 17.3, 20.6,...
## $ `TOV%`       <dbl> 18.0, 14.5, 13.2, 16.7, 21.7, 13.1, 22.9, 15.1, 1...
## $ `STL%`       <dbl> 0.0, 0.0, 1.7, 2.5, 1.5, 1.3, 1.7, 3.6, 1.9, 1.1,...
## $ `BLK%`       <dbl> 4.6, 0.0, 0.8, 2.6, 0.5, 1.2, 2.5, 0.2, 0.0, 0.0,...
## $ `USG%`       <dbl> 17.7, 24.5, 25.7, 15.1, 23.8, 18.7, 17.1, 18.5, 2...
## $ PPR          <dbl> -2.1, -7.0, -3.4, -0.9, -3.2, -3.0, -6.5, 0.7, 0....
## $ PPS          <dbl> 1.3, 1.3, 1.5, 1.2, 1.1, 1.5, 1.2, 1.5, 1.3, 1.2,...
## $ ORtg         <dbl> 107.0, 107.3, 116.5, 111.3, 93.9, 121.1, 93.8, 12...
## $ DRtg         <dbl> 111.9, 112.5, 108.1, 105.8, 106.6, 108.5, 104.5, ...
## $ eDiff        <dbl> -4.8, -5.2, 8.3, 5.5, -12.7, 12.6, -10.6, 19.5, 5...
## $ FIC          <dbl> 12.2, 6.5, 207.5, 83.5, 206.9, 243.1, 80.4, 152.9...
## $ PER          <dbl> 13.6, 15.8, 20.1, 14.7, 12.5, 18.3, 10.7, 19.2, 1...

4.3 ACB_Teams_18-19

# Estructura
glimpse(ACB_Teams_1819)
## Observations: 18
## Variables: 42
## $ Team               <chr> "Cafes Candelas Breogan", "Delteco GBC", "D...
## $ initials           <chr> "BRE", "GBC", "JOV", "FCB", "HGC", "TEN", "...
## $ GP                 <dbl> 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,...
## $ MPG                <dbl> 40.3, 40.4, 40.1, 40.1, 40.1, 40.9, 40.1, 4...
## $ FGM                <dbl> 28.1, 27.2, 29.0, 30.9, 30.2, 28.6, 28.1, 3...
## $ FGA                <dbl> 65.6, 62.1, 60.2, 61.6, 65.7, 60.3, 63.1, 6...
## $ `FG%`              <dbl> 0.428, 0.438, 0.481, 0.501, 0.460, 0.474, 0...
## $ `3PM`              <dbl> 7.9, 8.3, 9.4, 10.2, 9.7, 11.2, 9.3, 8.9, 8...
## $ `3PA`              <dbl> 24.7, 24.7, 25.1, 24.5, 27.4, 29.5, 26.5, 2...
## $ `3P%`              <dbl> 0.322, 0.335, 0.377, 0.418, 0.354, 0.378, 0...
## $ FTM                <dbl> 13.1, 12.6, 13.6, 14.7, 13.0, 13.2, 14.7, 1...
## $ FTA                <dbl> 18.3, 17.6, 17.4, 19.9, 17.2, 17.7, 20.5, 1...
## $ `FT%`              <dbl> 0.717, 0.717, 0.783, 0.741, 0.757, 0.749, 0...
## $ TOV                <dbl> 12.6, 13.5, 14.5, 12.1, 12.0, 11.9, 12.7, 1...
## $ PF                 <dbl> 21.5, 21.3, 20.9, 19.9, 22.1, 21.0, 21.6, 1...
## $ ORB                <dbl> 10.2, 8.7, 8.2, 9.1, 9.6, 8.2, 9.3, 8.2, 8....
## $ DRB                <dbl> 23.2, 21.3, 22.2, 24.6, 20.5, 20.5, 22.1, 2...
## $ RPG                <dbl> 33.4, 30.1, 30.4, 33.8, 30.1, 28.6, 31.4, 3...
## $ APG                <dbl> 14.1, 16.3, 16.5, 18.2, 15.8, 18.0, 14.5, 1...
## $ SPG                <dbl> 5.4, 7.0, 6.5, 7.0, 7.1, 6.5, 6.4, 8.0, 6.4...
## $ BPG                <dbl> 3.2, 1.8, 2.8, 2.4, 2.1, 1.8, 3.2, 2.9, 3.1...
## $ PPG                <dbl> 77.1, 75.2, 81.0, 86.7, 83.2, 81.5, 80.3, 8...
## $ `TS%`              <dbl> 0.524, 0.539, 0.597, 0.616, 0.567, 0.599, 0...
## $ `eFG%`             <dbl> 0.489, 0.504, 0.560, 0.584, 0.534, 0.566, 0...
## $ `Total.S%`         <dbl> 146.6, 149.0, 164.2, 166.0, 157.1, 160.1, 1...
## $ `ORB%`             <dbl> 29.1, 27.4, 28.2, 31.6, 29.5, 27.9, 28.1, 2...
## $ `DRB%`             <dbl> 73.4, 70.8, 70.1, 74.8, 72.5, 70.0, 70.8, 7...
## $ `TRB%`             <dbl> 50.1, 48.5, 50.0, 54.6, 49.6, 48.9, 48.8, 5...
## $ `AST%`             <dbl> 50.2, 59.8, 57.0, 58.9, 52.3, 63.1, 51.5, 6...
## $ `TOV%`             <dbl> 14.7, 16.2, 17.6, 14.7, 14.1, 14.9, 15.0, 1...
## $ `STL%`             <dbl> 7.4, 9.7, 8.9, 9.8, 9.6, 9.4, 8.7, 10.8, 8....
## $ `BLK%`             <dbl> 8.5, 4.9, 7.7, 7.3, 5.5, 5.2, 8.4, 7.9, 7.6...
## $ PPS                <dbl> 1.2, 1.2, 1.3, 1.4, 1.3, 1.4, 1.3, 1.4, 1.3...
## $ FIC40              <dbl> 47.7, 47.2, 54.8, 66.0, 54.1, 55.9, 51.6, 6...
## $ ORtg               <dbl> 104.7, 104.0, 112.1, 121.0, 113.0, 117.1, 1...
## $ DRtg               <dbl> 114.5, 113.5, 111.2, 106.3, 114.8, 114.4, 1...
## $ eDiff              <dbl> -9.8, -9.5, 0.9, 14.7, -1.8, 2.6, -2.5, 15....
## $ Poss               <dbl> 2505.5, 2459.9, 2457.5, 2437.0, 2502.8, 236...
## $ Pace               <dbl> 73.2, 71.6, 72.0, 71.4, 73.3, 68.2, 73.2, 7...
## $ Playoffs           <chr> "No clasificado", "No clasificado", "Clasif...
## $ `Puntos recibidos` <dbl> 84.35, 82.09, 80.38, 76.18, 84.50, 79.71, 8...
## $ Posicion           <dbl> 18, 17, 7, 2, 12, 9, 8, 3, 13, 10, 16, 1, 1...

5 Correlaciones

Estudiemos la correlación entre las diferentes variables estadísticas de los conjuntos de datos. Empezamos primero con el de los jugadores y después con el de los equipos (temporada 2018/19). En el caso de los equipos, no tenemos en cuentra los minutos y partidos, puesto que todos han jugado lo mismo.

5.1 Jugadores

# Correlaciones entre las estadísticas de los jugaroes (2018/19)
corr <- round(cor(ACB_Players_1819[, c(9:46)]), 1)
corrplot(corr, type="upper", method="color", tl.cex=0.7)

5.2 Equipos

# Correlaciones entre las estadísticas de los equipos (2018/19)
corr <- round(cor(ACB_Teams_1819[, c(5:39, 41)]), 1)
corrplot(corr, type="upper", method="color", tl.cex=0.7)

6 Jugadores y equipos más destacados (2018/19)

En esta sección vamos a conocer, primeramente, los jugadores más destacados durante la Liga Regular de la temporada 2018/19 en las categorías cuantificables más básicas e importantes del baloncesto: puntos, rebotes, asistencias, tapones, recuperaciones, pérdidas y faltas personales. También analizamos los porcentajes de los lanzamientos a canasta (desglosados en tiros libres, tiros de dos puntos y triples). Además, repetimos el mismo análisis para los equipos de la Liga Endesa.

6.1 Estadisticas individuales

6.1.1 Positivas

6.1.1.1 Puntos

# Top 5 anotadores en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10) %>%
  select(Player, PPG) %>%
  arrange(desc(PPG)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -PPG), y=PPG)) +
  geom_bar(aes(fill=PPG), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=PPG)) +
  scale_fill_gradient(low="darkseagreen3", high="darkseagreen1") +
  labs(title="Máximos anotadores de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Puntos por partido") +
  theme(panel.grid.major=element_blank(), 
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  ylim(0, 25)

# Imagen de Nicolas Laprovittola en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Laprovittola.png") 
grid.raster(image, x=0.18, y=0.75, height=0.2)

# Imagen de Jake Wiley en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Wiley.png") 
grid.raster(image, x=0.35, y=0.73, height=0.2)
  
# Imagen de Alessandro Gentile en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Gentile.png") 
grid.raster(image, x=0.53, y=0.7, height=0.2)

# Imagen de Askia Booker en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Booker.png") 
grid.raster(image, x=0.71, y=0.69, height=0.2)

# Imagen de Stan Okoye en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Okoye.png") 
grid.raster(image, x=0.88, y=0.68, height=0.2)

6.1.1.2 Rebotes

# Top 5 reboteadores en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10) %>%
  select(Player, RPG) %>%
  arrange(desc(RPG)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -RPG), y=RPG)) +
  geom_bar(aes(fill=RPG), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=RPG)) +
  scale_fill_gradient(low="darkseagreen3", high="darkseagreen1") +
  labs(title="Máximos reboteadores de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Rebotes por partido") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  ylim(0, 13)

# Imagen de Nik Caner-Medley en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Caner-Medley.png") 
grid.raster(image, x=0.18, y=0.74, height=0.2)

# Imagen de Edy Tavares en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Tavares.png") 
grid.raster(image, x=0.35, y=0.67, height=0.2)
  
# Imagen de Colton Iverson en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Iverson.png") 
grid.raster(image, x=0.53, y=0.64, height=0.2)

# Imagen de Gustavo Ayon en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Ayon.png") 
grid.raster(image, x=0.71, y=0.62, height=0.2)

# Imagen de Volodymyr Herun en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Herun.png") 
grid.raster(image, x=0.88, y=0.61, height=0.2)

6.1.1.3 Asistencias

# Top 5 asistentes en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10) %>%
  select(Player, APG) %>%
  arrange(desc(APG)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -APG), y=APG)) +
  geom_bar(aes(fill=APG), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=APG)) +
  scale_fill_gradient(low="darkseagreen3", high="darkseagreen1") +
  labs(title="Máximos asistentes de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Asistencias por partido") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  ylim(0, 9)

# Imagen de Nicolas Laprovittola en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Laprovittola.png") 
grid.raster(image, x=0.18, y=0.77, height=0.2)

# Imagen de Omar Cook en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Cook.png") 
grid.raster(image, x=0.35, y=0.77, height=0.2)
  
# Imagen de Alex Renfroe en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Renfroe.png") 
grid.raster(image, x=0.53, y=0.74, height=0.2)

# Imagen de Thomas Heurtel en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Heurtel.png") 
grid.raster(image, x=0.71, y=0.71, height=0.2)

# Imagen de Dani Perez en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Perez.png") 
grid.raster(image, x=0.88, y=0.68, height=0.2)

6.1.1.4 Tapones

# Top 5 taponadores en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10) %>%
  select(Player, BPG) %>%
  arrange(desc(BPG)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -BPG), y=BPG)) +
  geom_bar(aes(fill=BPG), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=BPG)) +
  scale_fill_gradient(low="darkseagreen3", high="darkseagreen1") +
  labs(title="Máximos taponadores de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Tapones por partido") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  ylim(0, 3)

# Imagen de Edy Tavares en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Tavares.png") 
grid.raster(image, x=0.17, y=0.74, height=0.2)

# Imagen de Alec Brown en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Brown.png") 
grid.raster(image, x=0.35, y=0.64, height=0.2)
  
# Imagen de Cady Lalanne en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Lalanne.png") 
grid.raster(image, x=0.52, y=0.56, height=0.2)

# Imagen de Vincent Poirier en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Poirier.png") 
grid.raster(image, x=0.7, y=0.54, height=0.2)

# Imagen de Marko Todorovic en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Todorovic.png") 
grid.raster(image, x=0.89, y=0.49, height=0.2)

6.1.1.5 Recuperaciones

# Top 5 ladrones en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10) %>%
  select(Player, SPG) %>%
  arrange(desc(SPG)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -SPG), y=SPG)) +
  geom_bar(aes(fill=SPG), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=SPG)) +
  scale_fill_gradient(low="darkseagreen3", high="darkseagreen1") +
  labs(title="Máximos recuperadores de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Tapones por partido") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  ylim(0, 3)

# Imagen de Dominique Sutton en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Sutton.png") 
grid.raster(image, x=0.17, y=0.69, height=0.2)

# Imagen de Luca Vildoza en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Vildoza.png") 
grid.raster(image, x=0.34, y=0.61, height=0.2)
  
# Imagen de Omar Cook en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Cook.png") 
grid.raster(image, x=0.52, y=0.59, height=0.2)

# Imagen de Adam Hanga en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Hanga.png") 
grid.raster(image, x=0.7, y=0.56, height=0.2)

# Imagen de Dani Perez en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Perez.png") 
grid.raster(image, x=0.88, y=0.56, height=0.2)

6.1.2 Negativas

6.1.2.1 Pérdidas

# Top 5 jugadores con más pérdidas en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10) %>%
  select(Player, TOV) %>%
  arrange(desc(TOV)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -TOV), y=TOV)) +
  geom_bar(aes(fill=TOV), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=TOV)) +
  scale_fill_gradient(low="tomato", high="tomato4") +
  labs(title="Jugadores con más pérdidas de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Pérdidas por partido") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  ylim(0, 7)

# Imagen de Nicolas Laprovvitola en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Laprovittola.png") 
grid.raster(image, x=0.17, y=0.75, height=0.2)

# Imagen de Alex Renfroe en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Renfroe.png") 
grid.raster(image, x=0.34, y=0.54, height=0.2)
  
# Imagen de Dominique Sutton en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Sutton.png") 
grid.raster(image, x=0.52, y=0.53, height=0.2)

# Imagen de Matic Rebec en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Rebec.png") 
grid.raster(image, x=0.7, y=0.51, height=0.2)

# Imagen de Alessandro Gentile en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Gentile.png") 
grid.raster(image, x=0.88, y=0.5, height=0.2)

6.1.2.2 Faltas personales

# Top 5 jugadores con más faltas personales en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10) %>%
  select(Player, PF) %>%
  arrange(desc(PF)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -PF), y=PF)) +
  geom_bar(aes(fill=PF), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=PF)) +
  scale_fill_gradient(low="tomato", high="tomato4") +
  labs(title="Jugadores con más faltas personales de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Faltas personales por partido") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  ylim(0, 5)

# Imagen de Nicolas Laprovvitola en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Dawson.png") 
grid.raster(image, x=0.17, y=0.74, height=0.2)

# Imagen de Jake Wiley en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Wiley.png") 
grid.raster(image, x=0.34, y=0.73, height=0.2)
  
# Imagen de Marko Todorovic en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Todorovic.png") 
grid.raster(image, x=0.52, y=0.72, height=0.2)

# Imagen de Sadiel Rojas en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Rojas.png") 
grid.raster(image, x=0.7, y=0.7, height=0.2)

# Imagen de Vincent Poirier en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Poirier.png") 
grid.raster(image, x=0.88, y=0.68, height=0.2)

6.1.3 Porcentajes de tiro

6.1.3.1 Tiros libres

# Top 5 jugadores con mejor porcentaje en tiros libres (FT%) en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10, FTM>1.5) %>%
  select(Player, `FT%`) %>%
  arrange(desc(`FT%`)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -`FT%`), y=`FT%`)) +
  geom_bar(aes(fill=`FT%`), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=paste0(round(`FT%`*100, 2), "%"))) +
  scale_fill_gradient(low="paleturquoise", high="paleturquoise4") +
  labs(title="Jugadores con mejor porcentaje en tiros libres (FT%) de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Porcentaje en tiros libres (FT%)") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  scale_y_continuous(labels=scales::percent, limits=c(0, 1.4)) 

# Imagen de Ryan Toolson en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Toolson.png") 
grid.raster(image, x=0.2, y=0.76, height=0.2)

# Imagen de Brian Roberts en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Roberts.png") 
grid.raster(image, x=0.37, y=0.74, height=0.2)
  
# Imagen de Marcus Eriksson en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Eriksson.png") 
grid.raster(image, x=0.54, y=0.73, height=0.2)

# Imagen de Jaycee Carroll en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Carroll.png") 
grid.raster(image, x=0.71, y=0.72, height=0.2)

# Imagen de Kostas Vasiliadis en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Vasiliadis.png") 
grid.raster(image, x=0.89, y=0.715, height=0.2)

6.1.3.2 Tiros de 2

# Top 5 jugadores con mejor porcentaje en tiros de 2 puntos (FG%) en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10, FGA>2.5) %>%
  select(Player, `FG%`) %>%
  arrange(desc(`FG%`)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -`FG%`), y=`FG%`)) +
  geom_bar(aes(fill=`FG%`), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=paste0(round(`FG%`*100, 2), "%"))) +
  scale_fill_gradient(low="paleturquoise", high="paleturquoise4") +
  labs(title="Jugadores con mejor porcentaje en tiros de 2 puntos (FG%) de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Porcentaje en tiros de 2 (FG%)") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  scale_y_continuous(labels=scales::percent, limits=c(0, 1.1)) 

# Imagen de Edy Tavares en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Tavares.png") 
grid.raster(image, x=0.2, y=0.75, height=0.2)

# Imagen de Emanuel Cate en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Cate.png") 
grid.raster(image, x=0.37, y=0.7, height=0.2)
  
# Imagen de Artem Pustovyi en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Pustovyi.png") 
grid.raster(image, x=0.54, y=0.69, height=0.2)

# Imagen de Ondrej Balvin en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Balvin.png") 
grid.raster(image, x=0.71, y=0.69, height=0.2)

# Imagen de Gustavo Ayon en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Ayon.png") 
grid.raster(image, x=0.89, y=0.68, height=0.2)

6.1.3.3 Triples

# Top 5 jugadores con mejor porcentaje en triples (3P%) en la temporada 2018/19
ACB_Players_1819 %>%
  filter(GP>10, `3PA`>2) %>%
  select(Player, `3P%`) %>%
  arrange(desc(`3P%`)) %>%
  head(n=5) %>%
  ggplot(aes(x=reorder(Player, -`3P%`), y=`3P%`)) +
  geom_bar(aes(fill=`3P%`), stat="identity", color="black", show.legend=FALSE) +
  geom_label(aes(label=paste0(round(`3P%`*100, 2), "%"))) +
  scale_fill_gradient(low="paleturquoise", high="paleturquoise4") +
  labs(title="Jugadores con mejor porcentaje en triples (3P%) de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Porcentaje en triples (3P%)") +
  theme(panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),
        panel.background=element_blank(), 
        axis.line=element_line(colour="black"),
        axis.title.x=element_blank()) +
  scale_y_continuous(labels=scales::percent, limits=c(0, 1)) 

# Imagen de Thaddeus McFadden en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/McFadden.png") 
grid.raster(image, x=0.2, y=0.61, height=0.2)

# Imagen de Trey Thompkins en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Thompkins.png") 
grid.raster(image, x=0.37, y=0.61, height=0.2)
  
# Imagen de Chris Singleton en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Singleton.png") 
grid.raster(image, x=0.54, y=0.6, height=0.2)

# Imagen de Matt Thomas en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Thomas.png") 
grid.raster(image, x=0.71, y=0.6, height=0.2)

# Imagen de Nick Zeisloft en la visualización
image <- image_read("C:/Users/xviva/Desktop/Xavier/Formacion/Data Science Awards 2019/Imagenes/Zeisloft.png") 
grid.raster(image, x=0.89, y=0.6, height=0.2)

6.2 Estadísticas de equipo

6.2.1 Positivas

6.2.1.1 Rebotes

# Equipos más reboteadores en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(RPG)) %>%
  ggplot(aes(x=reorder(Team, RPG), y=RPG)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=RPG)) +
  labs(title="Equipos más reboteadores de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Rebotes por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.1.2 Asistencias

# Equipos más asistentes en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(APG)) %>%
  ggplot(aes(x=reorder(Team, APG), y=APG)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=APG)) +
  labs(title="Equipos más asistentes de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Asistencias por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.1.3 Tapones

# Equipos más taponadores en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(BPG)) %>%
  ggplot(aes(x=reorder(Team, BPG), y=BPG)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=BPG)) +
  labs(title="Equipos más taponadores de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Tapones por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.1.4 Recuperaciones

# Equipos más recuperadores en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(SPG)) %>%
  ggplot(aes(x=reorder(Team, SPG), y=SPG)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=SPG)) +
  labs(title="Equipos con más recuperaciones de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Recuperaciones por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.2 Negativas

6.2.2.1 Pérdidas

# Equipos con más pérdidas en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(TOV)) %>%
  ggplot(aes(x=reorder(Team, TOV), y=TOV)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=TOV)) +
  labs(title="Equipos con más pérdidas de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Pérdidas por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.2.2 Faltas personales

# Equipos con más faltas personales en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(PF)) %>%
  ggplot(aes(x=reorder(Team, PF), y=PF)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=PF)) +
  labs(title="Equipos con más faltas personales de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Faltas personales por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.3 Porcentajes de tiro

6.2.3.1 Tiros libres

# Equipos con mejor porcentaje en tiros libres (FT%) en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(`FT%`)) %>%
  ggplot(aes(x=reorder(Team, `FT%`), y=`FT%`)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=paste0(round(`FT%`*100, 2), "%"))) +
  labs(title="Equipos con mejor FT% de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Porcentaje en tiros libres (FT%)") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.3.2 Tiros de 2

# Equipos con mejor porcentaje en tiros de 2 puntos (FG%) en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(`FG%`)) %>%
  ggplot(aes(x=reorder(Team, `FG%`), y=`FG%`)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=paste0(round(`FG%`*100, 2), "%"))) +
  labs(title="Equipos con mejor FG% de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Porcentaje en tiros de 2 (FG%)") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.3.3 Triples

# Equipos con mejor porcentaje en triples (3P%) en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(`3P%`)) %>%
  ggplot(aes(x=reorder(Team, `3P%`), y=`3P%`)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=paste0(round(`3P%`*100, 2), "%"))) +
  labs(title="Equipos con mejor 3P% de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Porcentaje en triples (3P%)") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.4 Ataque y defensa

6.2.4.1 Puntos anotados

# Equipos más ofensivos en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(PPG)) %>%
  ggplot(aes(x=reorder(Team, PPG), y=PPG)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=PPG)) +
  labs(title="Equipos más ofensivos de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Puntos anotados por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.2.4.2 Puntos recibidos

# Equipos más defensivos en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(`Puntos recibidos`)) %>%
  ggplot(aes(x=reorder(Team, -`Puntos recibidos`), y=`Puntos recibidos`)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=PPG)) +
  labs(title="Equipos más defensivos de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="Puntos recibidos por partido") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

6.3 Insights

Para obtener las visualizaciones superiores hemos filtrado las estadísticas a jugadores que hayan jugado más de 10 partidos, para evitar obtener resultados no significativos. Algunos insights:

  • Gran parte de los jugadores que aparecen en las visualizaciones de jugadores más destacados no van a continuar en el mismo equipo esta próxima temporada. La mayoría de ellos han jugado en equipos discretos, donde les es más fácil brillar y ser importantes, cuajar un buen año a nivel individual y revalorizarse para que puedan ser fichados por equipos más ambiciosos la próxima temporada. También encontramos el perfil de jugador de equipo grande, pero que por edad o por ser incapaz ya de mantener un nivel de exigencia alto, decide irse a un equipo acorde a sus características (Gustavo Ayon, por ejemplo). O Nik Caner-Medley, quien después de una larga carrera se retira del primer nivel para jugar en una liga menor como la de Japón. Veamos algunos casos,
Jugador Temporada 2018/19 Temporada 2019/20
Nicolas Laprovittola Club Joventut Badalona Real Madrid
Jake Wiley Herbalife Gran Canaria Panathinaikos
Stan Okoye Casademont Zaragoza Herbalife Gran Canaria
Nik Caner-Medley Movistar Estudiantes Cyberdyne Ibaraki Robots (Japón)
Colton Iverson Iberostar Tenerife Zenit de San Petersburgo
Gustavo Ayon Real Madrid Zenit de San Petersburgo
Volodymyr Herun Cafés Candelas Breogán Unicaja
Omar Cook Movistar Estudiantes Herbalife Gran Canaria
Alex Renfroe BAXI Manresa BC Zenit San Petersburgo
  • Movistar Estudiantes tiene hasta tres jugadores diferentes entre los mejores de tres apartados estadísticos diferentes: Omar Cook en asistencias, Alessandro Gentile en anotación y Nik Caner-Medley en rebotes. Vaya equipazo, puede pensar uno. Sin embargo, Movistar Estudiantes finalizó la temporada en undécima posición y con el objetivo prioritario de mantener la categoría. Ya lo suelen decir, que las individualidades no sirven de nada sin una cultura de trabajo en equipo.

  • Parece que a Joan Plaza le gusta mucho la Liga Endesa, puesto que hasta tres jugadores de los que aparecen en las visualizaciones superiores jugarán esta próxima temporada en el Zenit de San Petersburgo: Gustavo Ayon, Colton Iverson y Alex Renfroe. A parte de estos tres, también Andrew Albicy (MoraBanc Andorra), Tim Abromaitis (pareja interior junto con Colton Iverson en Iberostar Tenerife) y Will Thomas (Valencia Basket) se incorporarán al proyecto ruso. Casi nada, buen equipo para competir en la exigente Euroliga.

  • No hay mucho que comentar en cuanto a las estadísticas individuales. Los bases son los que reparten más asistencias, los pivots los que capturan más rebotes y colocan más tapones, los jugadores exteriores y ágiles los que recuperan más balones, etc. ¡Cómo siempre ha ocurrido en el baloncesto! Sin embargo, es curioso ver como hay cierta relación entre los jugadores más asistentes y los jugadores que más balones pierden (Nicolas Laprovittola y Alex Renfroe aparecen en las dos categorías). Suelen ser bases o jugadores con mucha responsabilidad ofensiva (Alessandro Gentile, por ejemplo). Jugadores que pasan mucho tiempo con el balón en las manos y que efectúan pases complicados para encontrar a sus compañeros en las mejores posiciones.

  • Los pivots, como es habitual, dominan el porcentaje de tiro de dos puntos. Sus tiros son muy cercanos o incluso justo debajo del aro. Además, suelen ser los jugadores que más mates efectúan, aumentando considerablemente su porcentaje.

  • ¿Qué se necesita para entrar en los Playoffs de la Liga Endesa? ¿Qué diferencia a un equipo de Playoff de otro equipo? Según las estadísticas, es imprescindible tener buenos porcentajes en tiros de dos y triples. En esta última temporada, 6 de los 8 primeros equipos en FG% y 3P% se clasificaron para Playoffs. Parece que también es importante cometer pocas faltas personales: los 5 primeros equipos en esta categoría consiguieron clasificarse.

  • Me ha sorprendido la estadística de las pérdidas. Parece que no es tan importante cuidar el balón y tener pocas pérdidas para clasificarse en los Playoffs. ¡Sólo 3 de los 9 primeros equipos en este apartado estadístico se clasificaron para Playoffs! ¡El Top 3 con menos pérdidas no consiguió clasificarse! ¿Qué puede decirnos esta estadística? Quizás que arriesgando se pueden obtener mejores resultados que siendo conservador.

  • ¡La defensa se impone al ataque! Parece que los equipos defensivos tienen más opciones de clasificarse en los Playoffs. De los 8 mejores equipos defensivos durante la Liga Regular, hasta 6 se clasificaron. Por otro lado, de los 8 mejores ofensivos, 5 lo lograron.

7 Porcentajes de tiro (2011/12 hasta 2017/18)

years <- c("2011/12", "2012/13", "2013/14", "2014/15",
           "2015/16", "2016/17", "2017/18")

# Porcentajes de tiro (temporada 2011/12 hasta 2017/18)
ACB_Players_1218 %>%
  mutate(Season=as.numeric(substr(temporada, 1, 4))) %>%
  group_by(Season) %>%
  summarise(`Tiros Libres`=mean(`FT%`),
            `Tiros de 2`=mean(`FG%`),
            `Triples`=mean(`3P%`)) %>%
  gather(Tiro, Porcentaje, 2:4) %>%
  ggplot(aes(x=Season, y=Porcentaje, colour=Tiro, group=Tiro)) +
  geom_line(size=1) +
  geom_point(aes(group=seq_along(Season)), size=2) +
  scale_x_continuous(breaks=c(2011:2017), labels=years) +
  scale_y_continuous(labels=scales::percent) +
  theme_bw() +
  theme(legend.title=element_blank(),
        legend.position="bottom") +
  labs(title="Porcentaje de tiro por temporada",
       subtitle="Temporada 2011/12 hasta 2017/18",
       x="Temporada") +
  transition_reveal(Season) 

No se aprecian diferencias significativas en los porcentajes de tiro a lo largo de las últimas temporadas.

8 Análisis por posiciones

8.1 Jugadores por posición

En esta sección vamos a estudiar el número de jugadores por posición a lo largo de las temporadas. Las posiciones especificadas en los datos proporcionados por el concurso son las siguientes: Center (C), Point-Guard (PG), Forward (F), Power-Forward (PF), Guard-Forward (GF), Shotting-Guard (SG), Small-Forward (SF), Foward-Center (FC) y Guard (G). Dicen que en el baloncesto moderno los pivots están desapareciendo, vamos a comprobar si es cierto o no.

Pues analizando el gráfico no se observa una tendencia de disminución o desaparición de los pivots en la Liga Endesa. Parece que de momento sólo en la NBA los pivots clásicos están llegando a su fin, dando paso a jugadores interiores más polivalentes, que incluso tiran triples y dan pases de canasta.

8.1.1 Gráfico animado

# Número de jugadores por posición (temporada 2011/12 hasta 2017/18)
ACB_Players_1218 %>%
  mutate(Season=as.numeric(substr(temporada, 1, 4))) %>%
  filter(Pos!="F-C", Pos!="G-F") %>%
  mutate(Pos=fct_recode(Pos, "Center"="C",
                        "Point-Guard"="PG",
                        "Forward"="F",
                        "Power-Forward"="PF",
                        "Guard-Forward"="GF",
                        "Shooting-Guard"="SG",
                        "Small-Forward"="SF",
                        "Forward-Center"="FC",
                        "Guard"="G")) %>%
  group_by(Pos, Season) %>% 
  summarise(Frecuencia=n()) %>%
  ggplot(aes(x=Season, y=Frecuencia, colour=Pos, group=Pos)) +
  geom_line(size=1) +
  geom_point(aes(group=seq_along(Season)), size=2) +
  scale_x_continuous(breaks=c(2011:2017), labels=years) +
  labs(x="Temporada", y="Frecuencia", title="Número de jugadores por posición",
       subtitle="Temporada 2011/12 hasta 2017/18") +
  theme_bw() +
  theme(legend.title=element_blank()) +
  transition_reveal(Season) 

8.1.2 Gráfico estático

# Número de jugadores por posición (temporada 2011/12 hasta 2017/18)
ACB_Players_1218 %>%
  filter(Pos!="F-C", Pos!="G-F") %>%
  mutate(Pos=fct_recode(Pos, "Center"="C",
                        "Point-Guard"="PG",
                        "Forward"="F",
                        "Power-Forward"="PF",
                        "Guard-Forward"="GF",
                        "Shooting-Guard"="SG",
                        "Small-Forward"="SF",
                        "Forward-Center"="FC",
                        "Guard"="G")) %>%
  group_by(Pos, temporada) %>% 
  summarise(Frecuencia=n()) %>%
  ggplot(aes(x=temporada, y=Pos)) +
  geom_tile(aes(fill=Frecuencia), stat="identity", color="black", show.legend=FALSE) +
  geom_text(aes(label=Frecuencia)) +
  scale_fill_gradient(low="white", high="firebrick3") +
  labs(x="Temporada", y="Posiciones", title="Número de jugadores por posición",
       subtitle="Temporada 2011/12 hasta 2017/18") +
  theme_bw() 

8.2 Estadísticas por posición

En este apartado vamos a analizar las principales estadísticas individuales medias por posición en la temporada 2018/19: puntos, rebotes, asistencias, recuperaciones, pérdidas de balón y faltas personales.

8.2.1 Individuales

# Estadísticas medias por posición en la temporada 2018/19 
ACB_Players_1819 %>%
  filter(Position!="F-C", Position!="G-F") %>%
  mutate(Position=fct_recode(Position, "Center"="C",
                             "Point-Guard"="PG",
                             "Forward"="F",
                             "Power-Forward"="PF",
                             "Guard-Forward"="GF",
                             "Shooting-Guard"="SG",
                             "Small-Forward"="SF",
                             "Forward-Center"="FC",
                             "Guard"="G")) %>%
  group_by(Position) %>%
  summarise(Puntos=round(mean(PPG), 2),
            Rebotes=round(mean(RPG), 2),
            Asistencias=round(mean(APG), 2),
            Recuperaciones=round(mean(SPG), 2),
            Tapones=round(mean(BPG), 2),
            `Pérdidas`=round(mean(TOV), 2),
            `Faltas personales`=round(mean(PF), 2)) %>%
  gather(Stat, Valores, 2:8) %>%
  ggplot(aes(x=Stat, y=Valores, fill=Position)) +
  geom_bar(stat="identity", color="black", show.legend=FALSE) +
  facet_wrap(~Position) +
  labs(title="Estadísticas individuales medias por posición", subtitle="Temporada 2018/19") +
  theme_bw() +
  theme(axis.title.x=element_blank(),
        axis.title.y=element_blank()) +
  coord_flip()

Aunque los escoltas son los jugadores que tienen una media de anotación ligeramente más alta, parece que jugadores de cualquier posición son capaces de anotar puntos. Por lo demás, los resultados son los esperados: los jugadores interiores colocan más tapones y capturan más rebotes, y los exteriores reparten más asistencias y recuperan más balones (también pierden más). En cuanto a faltas personales, encontramos bastante igualdad en todas las posiciones.

8.2.2 De tiro

# Porcentajes de tiro por posición en la temporada 2018/19
ACB_Players_1819 %>%
  filter(Position!="F-C", Position!="G-F") %>%
  mutate(Position=fct_recode(Position, "Center"="C",
                             "Point-Guard"="PG",
                             "Forward"="F",
                             "Power-Forward"="PF",
                             "Guard-Forward"="GF",
                             "Shooting-Guard"="SG",
                             "Small-Forward"="SF",
                             "Forward-Center"="FC",
                             "Guard"="G")) %>%
  group_by(Position) %>%
  summarise(`Tiros Libres %`=mean(`FT%`),
            `Tiros de 2 %`=mean(`FG%`),
            `Triples %`=mean(`3P%`)) %>%
  gather(Tiro, Valor, 2:4) %>%
  ggplot(aes(x=Position, y=Valor)) +
  geom_bar(stat="identity", color="black", aes(fill=Position), show.legend=FALSE) +
  facet_wrap(~Tiro) +
  labs(title="Porcentajes de tiro por posición", subtitle="Temporada 2018/19") +
  scale_y_continuous(labels=scales::percent) +
  theme_bw() +
  theme(axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        axis.text.x=element_text(angle=45, hjust=1)) 

Resultados bastante habituales en el mundo del baloncesto: los jugadores interiores son los que tienen un mayor porcentaje en tiros de dos puntos, mientras que los exteriores dominan en lanzamientos de larga distancia. Me ha sorprendido un detalle en el gráfico de tiros libres, y es que en esta última temporada 2018/19… ¡los aleros (Forward) han tenido un peor porcentaje que los pivots (Center)! Históricamente, muchos pivots son conocidos por sus mecánicas de tiro poco ortodoxas y sus bajos porcentajes en tiros libres. Shaquille O’neal, por ejemplo, uno de los mejores pivots de la historia de la NBA, falló 5.317 tiros libres a lo largo de toda su carrera.

8.2.3 Porcentajes de tiro por posición y por temporada

¿Qué les pasó a los aleros en el porcentaje de tiros libres de la temporada 2015/16 a la 2016/17? ¡Es una disminución importante!

8.2.3.1 Posiciones I

# Porcentajes de tiro por posición y por temporada 
ACB_Players_1218 %>%
  mutate(Season=as.numeric(substr(temporada, 1, 4))) %>%
  filter(Pos %in% c("PG", "G", "SG", "GF")) %>%
  mutate(Pos=fct_recode(Pos, "Point-Guard"="PG",
                             "Guard-Forward"="GF",
                             "Shooting-Guard"="SG",
                             "Guard"="G")) %>%
  group_by(Pos, Season) %>%
  summarise(`Tiros Libres`=mean(`FT%`),
            `Tiros de 2`=mean(`FG%`),
            `Triples`=mean(`3P%`)) %>%
  gather(Tiro, Porcentaje, 3:5) %>%
  ggplot(aes(x=Season, y=Porcentaje, colour=Tiro, group=Tiro)) +
  geom_line(size=1) +
  geom_point(aes(group=seq_along(Season)), size=2) +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(breaks=c(2011:2017), labels=years) +
  theme_bw() +
  theme(legend.title=element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.position="bottom") +
  facet_grid(Pos ~ .) +
  labs(title="Porcentajes de tiro por posición y por temporada",
       subtitle="Temporada 2011/12 hasta 2017/18") +
  transition_reveal(Season) 

8.2.3.2 Posiciones II

# Porcentajes de tiro por posición y por temporada 
ACB_Players_1218 %>%
  mutate(Season=as.numeric(substr(temporada, 1, 4))) %>%
  filter(Pos %in% c("C", "F", "PF", "SF", "FC")) %>%
  mutate(Pos=fct_recode(Pos, "Center"="C",
                             "Forward"="F",
                             "Power-Forward"="PF",
                             "Small-Forward"="SF",
                             "Forward-Center"="FC",)) %>%
  group_by(Pos, Season) %>%
  summarise(`Tiros Libres`=mean(`FT%`),
            `Tiros de 2`=mean(`FG%`),
            `Triples`=mean(`3P%`)) %>%
  gather(Tiro, Porcentaje, 3:5) %>%
  ggplot(aes(x=Season, y=Porcentaje, colour=Tiro, group=Tiro)) +
  geom_line(size=1) +
  geom_point(aes(group=seq_along(Season)), size=2) +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(breaks=c(2011:2017), labels=years) +
  theme_bw() +
  theme(legend.title=element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.position="bottom") +
  facet_grid(Pos ~ .) +
  labs(title="Porcentajes de tiro por posición y por temporada",
       subtitle="Temporada 2011/12 hasta 2017/18") +
  transition_reveal(Season) 

9 Nacionalidades (2011/12 hasta 2017/18)

Una de las críticas que suele recibir la Liga Endesa estas últimas temporadas es acerca de los jugadores nacionales: “¡Se está perdiendo el talento nacional!”, “¡Las canteras no dan oportunidades a los jóvenes jugadores españoles de subir al primer equipo!”, “El futuro de la selección española es muy negro!” (y ahí están, acabadísimos, ganando Mundiales y con el billete para las Olimpiadas de Tokyo conseguido), etc. Veamos si estas críticas son acertadas o no.

Pues… están en lo cierto. Si nos fijamos en el segundo gráfico de esta sección, a partir de la temporada 2013/14 se produce un importante decrecimiento de jugadores nacionales hasta la temporada 2016/17 (de 102 a 61). Aunque los dos últimos años esta cifra ha incrementado un poco, todavía no hemos vuelto a alcanzar el máximo de 2016/17.

Y pensando un poco se me ocurren dos ejemplos actuales que escenifican perfectamente este problema:

Parece que los clubs prefieren fichar a jugadores extranjeros ya consagrados, buscando el rendimiento inmediato, antes que apostar por la cantera. Aunque dar oportunidades a los jóvenes pueda ser arriesgado a corto plazo, con paciencia y tiempo seguro que se obtendrían buenos resultados. Si esta misma tendencia hubiera pasado allá por finales de los 90, ¿qué habría sido de Pau Gasol o Juan Carlos Navarro, por ejemplo? ¿Puede que nos estemos perdiendo al próximo Pau Gasol o Juan Carlos Navarro?

9.1 Temporada 2018/19

# Nacionalidades en la temporada 2018/19
ACB_Players_1819 %>%
  count(Nationality) %>%
  arrange(desc(n)) %>%
  head(n=10) %>%
  ggplot(aes(x=reorder(Nationality, n), y=n)) +
  geom_bar(stat="identity", aes(fill=n), color="black", show.legend=FALSE) +
  geom_label(aes(label=n)) +
  scale_fill_gradient(low="paleturquoise", high="paleturquoise4") +
  labs(x="Nacionalidad", title="Nacionalidad de los jugadores",
       subtitle="Temporada 2018/19") +
  theme_bw() +
  theme(axis.title.x=element_blank()) +
  coord_flip()

9.2 Temporada 2011/12 hasta 2017/18

# Nacionalidades (temporada 2011/12 hasta 2017/18)
ACB_Players_1218 %>%
  mutate(Season=as.numeric(substr(temporada, 1, 4))) %>%
  filter(Nationality %in% c("Spain", "United Stated", "Serbia", "France",
                            "Brazil", "Argentina", "Croatia", "Senegal",
                            "Lithuania", "Czech Republic")) %>%
  group_by(Nationality, Season) %>%
  summarise(Count=n()) %>%
  ggplot(aes(x=Season, y=Count, colour=Nationality, group=Nationality)) +
  geom_line(size=1) +
  geom_point(aes(group=seq_along(Season)), size=2) +
  scale_x_continuous(breaks=c(2011:2017), labels=years) +
  theme_bw() +
  theme(legend.title=element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank()) +
  labs(title="Nacionalidad de los jugadores",
       subtitle="Temporada 2011/12 hasta 2017/18") +
  transition_reveal(Season)  

10 Peso y altura (2018/19)

En esta sección vamos a estudiar la distribución del peso y altura de los jugadores de la Liga Endesa. Podemos analizar entre qué rango de pesos y alturas se mueven los jugadores, en función de su posición: por lo general, los jugadores interiores son más altos y pesados, mientras que los exteriores son más bajitos y ligeros.

Para la elaboración de estos gráficos hemos simplificado las posiciones para mejorar la visualización, teniendo en cuenta sólo las principales: Point-Guard, Small-Forward, Shooting-Guard, Power-Forward y Center.

10.1 Peso

# Distribución del peso de los jugadores en la temporada 2018/19
ACB_Players_1819 %>%  
  filter(Position!="F-C", Position!="G-F") %>%
  mutate(Position=fct_recode(Position, "Center"="C",
                             "Point-Guard"="PG",
                             "Small-Forward"="F",
                             "Power-Forward"="PF",
                             "Shooting-Guard"="GF",
                             "Shooting-Guard"="SG",
                             "Small-Forward"="SF",
                             "Center"="FC",
                             "Point-Guard"="G")) %>%
  ggplot(aes(x=Peso, fill=Position)) +
  geom_density(alpha=0.5) +
  labs(x="Peso (kg)", y="Densidad", title="Distribución del peso de los jugadores",
       subtitle="Temporada 2018/19") +
  theme_bw() +
  theme(legend.title=element_blank(),
        legend.position="bottom") +
  xlim(60, 130)

10.2 Altura

# Distribución de la altura de los jugadores en la temporada 2018/19
ACB_Players_1819 %>%  
  filter(Position!="F-C", Position!="G-F") %>%
  mutate(Position=fct_recode(Position, "Center"="C",
                             "Point-Guard"="PG",
                             "Small-Forward"="F",
                             "Power-Forward"="PF",
                             "Shooting-Guard"="GF",
                             "Shooting-Guard"="SG",
                             "Small-Forward"="SF",
                             "Center"="FC",
                             "Point-Guard"="G")) %>%
  ggplot(aes(x=altura, fill=Position)) +
  geom_density(alpha=0.5) +
  labs(x="Altura (cm)", y="Densidad", title="Distribución de la altura de los jugadores",
       subtitle="Temporada 2018/19") +
  theme_bw() +
  theme(legend.title=element_blank(),
        legend.position="bottom") 

11 Clusterización de jugadores

En este apartado vamos a llevar a cabo una categorización o clusterización de jugadores en base a su estilo de juego/estadísticas para encontrar jugadores que compartan patrones de juego. Para ello, utilizaremos el metodo de k-means. Se trata de un algoritmo de aprendizaje no supervisado (no hay un conocimiento a priori) que tiene como objetivo la partición de un conjunto de n observaciones en k grupos o clusters.

11.1 Normalización

Antes de iniciar el proceso de segmentación es necesario normalizar los valores de las variables para eliminar el efecto de las distintas escalas de medida. Esto equivale a restarles su media y dividirlas por su desviación estándar (función scale() en R).

Vamos a utilizar todas las columnas numéricas, excepto la altura y peso de los jugadores. Es decir, incorporamos aquellas variables que forman parte de la analítica del baloncesto (puntos, minutos, rebotes, asistencias, estadísticas de analítica avanzada, etc.)

# Normalización de los atributos 
ACB_Players_1819_Norm <- as.data.frame(scale(ACB_Players_1819[, -c(1:8)]))

# Datos originales
p1 <- ggplot(ACB_Players_1819, aes(x=PPG, y=RPG)) +
  geom_point() +
  labs(title="Datos originales", x="Puntos por partido",
       y="Rebotes por partido") +
  theme_bw()

# Datos normalizados 
p2 <- ggplot(ACB_Players_1819_Norm, aes(x=PPG, y=RPG)) +
  geom_point() +
  labs(title="Datos normalizados", x="Puntos por partido",
       y="Rebotes por partido") +
  theme_bw()

# Subplot
grid.arrange(p1, p2, ncol=2)

11.2 Elección de clusters

Una segmentación óptima es aquella donde los individuos pertenecientes a un mismo grupo son lo más homogéneos posible y los individuos pertenecientes a distintos grupos son lo más heterogéneos posible. La varianza dentro de grupos debe ser reducida (individuos dentro de un mismo grupo tienen que ser similares) y la varianza entre grupos debe ser grande (individuos de distintos grupos tienen que ser distintos).

A continuación definimos el modo de obtener un gráfico que nos represente la varianza entre grupos y dentro de grupos en función del número de grupos.

bss <- numeric()
wss <- numeric()

# Semilla
set.seed(800)

# Ejecutamos el algoritmo k-means para diferentes valores de k
for(i in 1:10){

  # Para cada k, calculamos betweenss y tot.withinss
  bss[i] <- kmeans(ACB_Players_1819_Norm, centers=i)$betweenss
  wss[i] <- kmeans(ACB_Players_1819_Norm, centers=i)$tot.withinss

}

# Creación de data frame
df <- data.frame(k=1:10, bss, wss)
df <- df %>% 
  gather(Varianzas, Valores, 2:3) %>%
  mutate(Varianzas=fct_recode(Varianzas, "Varianza dentro de grupos"="wss",
                              "Varianza entre grupos"="bss"))

# Varianza entre grupos y dentro de grupos vs Elección de k
df %>%
  ggplot(aes(x=k, y=Valores, colour=Varianzas)) +
  geom_line() +
  geom_point(size=2) +
  labs(title="Varianza entre grupos y dentro de grupos vs Elección de k", 
       y="Varianza", x="Número de clusters", subtitle="Método del codo") +
  scale_x_continuous(breaks=1:10) +
  geom_vline(xintercept=5, linetype="dotted") +
  theme_bw() +
  theme(legend.position="bottom",
        legend.title=element_blank())

Para seleccionar el valor óptimo de k, se escoje ese punto en donde se dejan de producir variaciones significativas de los valores de varianza entre grupos y dentro de grupos (método del codo). En este caso, observando el gráfico no queda nada claro qué número de clusters sería el ideal, ya que no se aprecia con claridad ningún “codo” en las líneas. Aún así, después de ejecutar el algoritmo con diferentes valores y visualizar los resultados para cada ejecución, creemos que con 5 grupos se puede llevar a cabo una buena segmentación.

11.3 Resultados

A continuación podemos ver gráficamente la partición de los datos para diferentes valores de k. Hemos utilizado la función fviz_cluster(), que describe el conjunto de datos inicial en términos de nuevas variables (componentes) mediante la técnica PCA. En las visualizaciones, los datos están representados por las dos primeras componentes (Dim1 y Dim2).

# Semilla
set.seed(800)

# Ejecución del algoritmo k-means
kmeans2 <- kmeans(ACB_Players_1819_Norm, centers=2)
kmeans3 <- kmeans(ACB_Players_1819_Norm, centers=3)
kmeans4 <- kmeans(ACB_Players_1819_Norm, centers=4)
kmeans5 <- kmeans(ACB_Players_1819_Norm, centers=5)

# Nueva columna con la asignación de los clusters a cada jugador 
ACB_Players_1819$Cluster <- kmeans5$cluster

# Visualización
p1 <- fviz_cluster(kmeans2, geom="point", data=ACB_Players_1819_Norm) + ggtitle("K=2") + theme_bw()
p2 <- fviz_cluster(kmeans3, geom="point", data=ACB_Players_1819_Norm) + ggtitle("K=3") + theme_bw()
p3 <- fviz_cluster(kmeans4, geom="point", data=ACB_Players_1819_Norm) + ggtitle("K=4") + theme_bw()
p4 <- fviz_cluster(kmeans5, geom="point", data=ACB_Players_1819_Norm) + ggtitle("K=5") + theme_bw()

# Subplot
grid.arrange(p1, p2, p3, p4, nrow=2)

¿Cómo luce la partición en nuestros datos de jugadores? En el siguiente gráfico lo podemos comprobar. No hemos utilizado todas las variables para favorecer una mejor visualización.

# Clustering 
ggpairs(ACB_Players_1819, 
        columns=10:17, aes(colour=as.factor(Cluster), alpha=0.5),
        lower=list(continuous="points"),
        upper=list(continuous="blank"),
        axisLabels="none", switch="both") +
        theme_bw()

12 Clusterización de equipos

En este apartado vamos a seguir los mismos pasos que en la sección anterior, pero esta vez realizando una segmentación para los equipos de la Liga Endesa (temporada 2018/19). No vamos a utilizar las columnas de minutos por partido y partidos jugados, puesto que todos los equipos han jugado lo mismo.

En este caso no incluiremos tantos detalles durante el proceso de clusterización, puesto que ya lo hemos hecho anteriormente. Empezamos con la normalización de los datos:

# Normalización de los atributos 
ACB_Teams_1819_Norm <- as.data.frame(scale(ACB_Teams_1819[, -c(1:4, 40)]))

Toca ahora decidir qué número de clusters escoger para la segmentación. Esperemos que el método del codo funcione mejor.

bss <- numeric()
wss <- numeric()

# Semilla
set.seed(123)

# Ejecutamos el algoritmo k-means para diferentes valores de k
for(i in 1:10){

  # Para cada k, calculamos betweenss y tot.withinss
  bss[i] <- kmeans(ACB_Teams_1819_Norm, centers=i)$betweenss
  wss[i] <- kmeans(ACB_Teams_1819_Norm, centers=i)$tot.withinss

}

# Creación de data frame
df <- data.frame(k=1:10, bss, wss)
df <- df %>% 
  gather(Varianzas, Valores, 2:3) %>%
  mutate(Varianzas=fct_recode(Varianzas, "Varianza dentro de grupos"="wss",
                              "Varianza entre grupos"="bss"))

# Varianza entre grupos y dentro de grupos vs Elección de k
df %>%
  ggplot(aes(x=k, y=Valores, colour=Varianzas)) +
  geom_line() +
  geom_point(size=2) +
  labs(title="Varianza entre grupos y dentro de grupos vs Elección de k", 
       y="Varianza", x="Número de clusters", subtitle="Método del codo") +
  scale_x_continuous(breaks=1:10) +
  geom_vline(xintercept=4, linetype="dotted") +
  theme_bw() +
  theme(legend.position="bottom",
        legend.title=element_blank())

Menos mal, en este caso, parece claro que a partir de 4 clusters las variaciones de los valores de varianza entre grupos y dentro de grupos dejan de ser significativas.

Pues finalmente toca ejecutar el algoritmo y estudiar visualmente la partición:

# Semilla
set.seed(123)

# Ejecución del algoritmo k-means
kmeans2 <- kmeans(ACB_Teams_1819_Norm, centers=2)
kmeans3 <- kmeans(ACB_Teams_1819_Norm, centers=3)
kmeans4 <- kmeans(ACB_Teams_1819_Norm, centers=4)
kmeans5 <- kmeans(ACB_Teams_1819_Norm, centers=5)

# Nueva columna con la asignación de los clusters a cada jugador 
ACB_Teams_1819$Cluster <- kmeans4$cluster

# Visualización
p1 <- fviz_cluster(kmeans2, geom="point", data=ACB_Teams_1819_Norm) + ggtitle("K=2") + theme_bw()
p2 <- fviz_cluster(kmeans3, geom="point", data=ACB_Teams_1819_Norm) + ggtitle("K=3") + theme_bw()
p3 <- fviz_cluster(kmeans4, geom="point", data=ACB_Teams_1819_Norm) + ggtitle("K=4") + theme_bw()
p4 <- fviz_cluster(kmeans5, geom="point", data=ACB_Teams_1819_Norm) + ggtitle("K=5") + theme_bw()

# Subplot
grid.arrange(p1, p2, p3, p4, nrow=2)

Veamos los resultados con un poco más de detalle:

# Labels como rownames
rownames(ACB_Teams_1819_Norm) <- ACB_Teams_1819$Team
  
# Clusters con los labels
fviz_cluster(kmeans4, data=ACB_Teams_1819_Norm, star.plot=TRUE, repel=TRUE) + 
  ggtitle("K=4") + 
  theme_bw() +
  theme(legend.position="none")

Vamos a comprobar algo curioso, si existe alguna relación entre la posición en la que han quedado los equipos de la Liga Endesa esta última temporada y el cluster al que pertenecen.

# Relación entre la posición de los equipos y el cluster al que pertenecen 
ACB_Teams_1819 %>%
  mutate_at(vars(Cluster), factor) %>%
  ggplot(aes(x=reorder(Team, -Posicion), y=Posicion, label=Posicion)) + 
  geom_point(stat="identity", aes(col=Cluster), size=6.5)  +
  geom_text(color="white", size=3) +
  labs(title="Relación posición y cluster al que pertencen", 
       subtitle="Temporada 2018/19", y="Posición en la Liga Endesa") + 
  coord_flip() +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom")

¡Pues sí! Podríamos decir que la categorización ha dividido los equipos en nivel alto (equipos de Euroliga), medio-alto, medio y bajo. El caso más extraño es el de Rio Natura Monbus Obradoiro, categorizado en el cluster 4 (equipos de nivel medio-alto), pero clasificado cuarto por la cola en la Liga Endesa. Puede ser que su forma de jugar y estadísticas sean de equipo de nivel medio-alto, pero los resultados no le hayan acompañado esta última temporada.

13 Variables más influyentes

En este apartado vamos a descubrir qué características son las más importantes de cara a clasificarse o no en los Playoffs de la Liga Endesa, incluyendo en el análisis atributos de estadística avanzada. Los árboles de decisión son capaces, en el momento de creación del modelo, de puntuar las variables más importantes de acuerdo a cómo son de idóneas para crear el corte que separa un conjunto de datos en dos subconjuntos.

En el primer apartado del reto intentamos adivinar, más a ojo, analizando los gráficos, qué variables son las más importantes de cara a lograr la clasificación para los Playoffs. En este apartado vamos a basarnos en un modelo, más fiable y preciso.

# Columnas a tener en cuenta
variables <- ACB_Teams_1819[, 5:41]

# Variable objetivo como categórica
variables <- variables %>%
  mutate_at(vars(Playoffs), factor)

# Variable control
control <- trainControl(method="repeatedcv", number=30, repeats=10)

# Modelo
model <- train(Playoffs~., data=variables, method="lvq", preProcess="scale", trControl=control)

Una vez creado el modelo, calculamos la importancia de las variables mediante la función varImp() de R. Hemos clasificado las estadísticas como básicas o avanzadas siguiendo el documento “Dataset-Variables-Description.docx” que nos proporciona el concurso.

# Variables mas importantes
imp <- as.data.frame(varImp(model, scale=FALSE)$importance)
imp$Stat <- rownames(imp)
rownames(imp) <- 1:nrow(imp)

# Nueva columna para indicar si se trata de un indicador de estadística básica o avanzada
imp$`Estadística` <- c(rep("Estadística básica", 18), rep("Estadística avanzada", 11), 
                       "Estadística básica", rep("Estadística avanzada", 5), "Estadística básica")

# Visualización
imp %>%
  arrange(desc(Clasificado)) %>%
  head(n=15) %>%
  ggplot(aes(x=reorder(Stat, Clasificado), y=Clasificado)) +
  geom_bar(stat="identity", aes(fill=`Estadística`)) +
  geom_label(aes(label=round(Clasificado, 2))) +
  coord_flip() +
  theme_bw() +
  labs(title="Variables más influyentes para entrar en Playoffs",
       subtitle="Temporada 2018/19", x="Indicador", y="Importancia") +
  theme(legend.title=element_blank(),
        legend.position="bottom")

Si un equipo de la Liga Endesa quiere clasificarse para los Playoffs, ya pueden los jugadores ponerse a practicar los tiros de dos puntos (FG%), ya que parece que se trata de un indicador imprescindible. Los tres siguientes en la clasificación son atributos de estadística avanzada:

Parece que una buena parte de los indicadores más importantes dependen de otros (se calculan como fórmulas), de manera que involucran varios aspectos del juego a la vez, y por esa razón es posible que tomen más importancia. ¿Qué hay de las variables menos influyentes? Veamos,

# Variables menos influyentes
imp %>%
  arrange(desc(Clasificado)) %>%
  tail(n=15) %>%
  ggplot(aes(x=reorder(Stat, -Clasificado), y=Clasificado)) +
  geom_bar(stat="identity", aes(fill=`Estadística`)) +
  geom_label(aes(label=round(Clasificado, 2))) +
  coord_flip() +
  theme_bw() +
  labs(title="Variables menos influyentes para entrar en Playoffs",
       subtitle="Temporada 2018/19", x="Indicador", y="Importancia") +
  theme(legend.title=element_blank(),
        legend.position="bottom")

¿Cuál podría ser un posible árbol de decisión que nos permitiera predecir si un equipo va a clasificarse en Playoffs o no? En el siguiente trozo de código contruimos uno utilizando el paquete C50. Si tuvieramos las estadísticas de los equipos durante la Liga Regular 2019/20, por ejemplo, podríamos pronosticar si se clasificarían para Playoffs o no, utilizando los datos de este reto como entrenamiento del modelo.

# Árbol de decisión
model <- C50::C5.0(variables[, c(1:35, 37)],as.factor(variables$Playoffs))

# Summary 
summary(model)
## 
## Call:
## C5.0.default(x = variables[, c(1:35, 37)], y
##  = as.factor(variables$Playoffs))
## 
## 
## C5.0 [Release 2.07 GPL Edition]      Fri Sep 20 23:08:08 2019
## -------------------------------
## 
## Class specified by attribute `outcome'
## 
## Read 18 cases (37 attributes) from undefined.data
## 
## Decision tree:
## 
## Total.S% > 160.1: Clasificado (6)
## Total.S% <= 160.1:
## :...BLK% <= 7.9: No clasificado (9)
##     BLK% > 7.9: Clasificado (3/1)
## 
## 
## Evaluation on training data (18 cases):
## 
##      Decision Tree   
##    ----------------  
##    Size      Errors  
## 
##       3    1( 5.6%)   <<
## 
## 
##     (a)   (b)    <-classified as
##    ----  ----
##       8          (a): class Clasificado
##       1     9    (b): class No clasificado
## 
## 
##  Attribute usage:
## 
##  100.00% Total.S%
##   66.67% BLK%
## 
## 
## Time: 0.0 secs

La estructura del árbol creado es muy simple, utilizando sólo dos variables (Total.S% y BLK%) y cometiendo un error de clasificación.

14 Estadística avanzada

Veamos qué diferencias se producen en algunas variables de estadística avanzada en equipos ganadores y perdedores (clasificados o no clasificados para Playoffs).

# Distribución de TRB%, AST%, TOV%, STL% y BLK% 
ACB_Teams_1819 %>%
  select(`TRB%`, `AST%`, `TOV%`, `STL%`, `BLK%`, Playoffs) %>%
  gather(Stat, Values, 1:5) %>%
  ggplot(aes(x=Values/100, fill=Playoffs)) +
  geom_density(alpha=0.5) +
  facet_wrap(~Stat, scales="free") +
  scale_x_continuous(labels=scales::percent) +
  theme_bw() +
  theme(legend.position="bottom",
        axis.text.x=element_text(angle=45, hjust=1),
        axis.title.x=element_blank()) +
  labs(y="Densidad", title="Estadística avanzada", subtitle="Temporada 2018/19")

# Equipos con más eDiff en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(eDiff)) %>%
  ggplot(aes(x=reorder(Team, eDiff), y=eDiff)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=eDiff)) +
  labs(title="Equipos con más eDiff de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="eDiff") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

# Equipos con más TRB% en la temporada 2018/19
ACB_Teams_1819 %>%
  arrange(desc(`TRB%`)) %>%
  ggplot(aes(x=reorder(Team, `TRB%`), y=`TRB%`)) +
  geom_bar(aes(fill=Playoffs), stat="identity", color="black") +
  geom_label(aes(label=`TRB%`)) +
  labs(title="Equipos con más TRB% de la Liga Endesa", 
       subtitle="Temporada 2018/19", y="TRB%") +
  theme_bw() +
  theme(axis.title.y=element_blank(),
        legend.position="bottom") +
  coord_flip()

Por llegar un poco apurado a la entrega no he podido analizar más indicadores de estadística avanzada ni sacar las conclusiones o insights pertinentes en este apartado.

15 Creación de KPI

En el conjunto de datos proporcionado he visto dos métricas que indican el rendimiento de los jugadores, calculadas mediante fórmulas que envuelven todos los aspectos del juego. Éstas son:

Sin embargo, no he visto nada de la valoración. La valoración es una fórmula estadística utilizada mayormente en Europa, similar a la eficiencia utilizada en la NBA, para medir el rendimiento de jugadores. Básicamente se calcula como la diferencia entre la suma de las estadísticas positivas y la suma de las estadísticas negativas. Dicho de otra forma:

Valoración = (Puntos + Rebotes + Asistencias + Robos + Tapones + Faltas Recibidas) - (Tiros de Campo Fallados + Tiros Libres Fallados + Tapones Recibidos + Pérdidas + Faltas Realizadas)

En nuestro conjunto de datos no disponemos de las faltas recibidas ni tampoco de los tapones recibidos, pero no tiene importancia, puede calcularse el indicador con las variables disponibles. En cuanto a los tiros fallados, los podemos calcular como la diferencia entre los tiros intentados y los tiros que acaban en canasta: FGA-FGM para los tiros de 2, 3PA-3PM para los triples y FTA-FTM para los tiros libres.

# Creación de la estadística "Valoración" y visualización
ACB_Players_1819 %>%
  mutate(Valoracion=(PPG+RPG+APG+SPG+BPG)-((FGA-FGM)+(`3PA`-`3PM`)+(FTA-FTM)+TOV+PF)) %>%
  arrange(desc(Valoracion)) %>%
  select(Player, Valoracion) %>%
  head(20) %>%
  ggplot(aes(x=reorder(Player, Valoracion), y=Valoracion)) +
  geom_bar(stat="identity", color="black", aes(fill=Valoracion), show.legend=FALSE) +
  scale_fill_gradient(low="paleturquoise", high="paleturquoise4") +
  geom_label(aes(label=Valoracion)) +
  theme_bw() +
  theme(axis.title.y=element_blank()) +
  coord_flip() +
  labs(title="Jugadores más valorados", subtitle="Temporada 2018/19",
       y="Valoración")

¿Què hay de los equipos? ¿Qué equipos tienen una valoración por partido más alta?

# Equipos más valorados
ACB_Teams_1819 %>%
  mutate(Valoracion=(PPG+RPG+APG+SPG+BPG)-((FGA-FGM)+(`3PA`-`3PM`)+(FTA-FTM)+TOV+PF)) %>%
  arrange(desc(Valoracion)) %>%
  select(Team, Valoracion, Playoffs) %>%
  ggplot(aes(x=reorder(Team, Valoracion), y=Valoracion)) +
  geom_bar(stat="identity", color="black", aes(fill=Playoffs)) +
  geom_label(aes(label=Valoracion)) +
  theme_bw() +
  theme(legend.position="bottom",
        axis.title.y=element_blank()) +
  coord_flip() +
  labs(title="Equipos más valorados", subtitle="Temporada 2018/19", y="Valoración")

Aunque el cálculo de la valoración no sea preciso del todo (nos faltan tapones y faltas recibidas), parece que es un indicador muy fiable de cara a pronosticar si un equipo va a clasificarse en los Playoffs de la Liga Endesa o no. 8 de los 9 primeros equipos mejor valorados se clasificaron esta última temporada 2018/19. Curioso el caso del Manresa, que a pesar de ser de los equipos peores en valoración, consiguió ser uno de los 8 mejores equipos del año.

16 Comentarios finales

Por último, comentar que he disfrutado mucho elaborando este proyecto, teniendo la posibilidad de combinar el análisis de datos con el baloncesto… ¡qué más puedo pedir! :) Me hubiera gustado extraer más conclusiones o insights en algún que otro apartado (en el de correlaciones o estadística avanzada, por ejemplo), como también analizar conjuntos de datos externos a los proporcionados por el reto para enriquecer más el análisis (jugada a jugada, estadísticas de jugadores o equipos partido a partido, etc.), y bueno… muchas otras cosas, pero al final el tiempo se me echó un poco encima. Independientemente del resultado, estoy seguro que el año que viene volveré a participar. ¡Es una iniciativa genial y se la recomendaría a cualquier persona interesada en la Ciencia de Datos!

17 Referencias a librerías utilizadas

Alexander Walker (2019). openxlsx: Read, Write and Edit XLSX Files. R package version 4.1.0.1. https://CRAN.R-project.org/package=openxlsx

Hadley Wickham (2017). tidyverse: Easily Install and Load the ‘Tidyverse’. R package version 1.2.1 https://CRAN.R-project.org/package=tidyverse

Yihui Xie (2019). knitr: A General-Purpose Package for Dynamic Report Generation in R. R package version 1.24.

Jeroen Ooms (2019). magick: Advanced Graphics and Image-Processing in R. R package version 2.2. https://CRAN.R-project.org/package=magick

R Core Team (2019). R: A language and environment for statistical computing. R Foundation for Statistical Computing, Vienna, Austria. URL https://www.R-project.org/

Carson Sievert (2018) plotly for R. https://plotly-r.com

Thomas Lin Pedersen and David Robinson (2019). gganimate: A Grammar of Animated Graphics. R package version 1.0.3. https://CRAN.R-project.org/package=gganimate

Baptiste Auguie (2017). gridExtra: Miscellaneous Functions for “Grid” Graphics. R package version 2.3. https://CRAN.R-project.org/package=gridExtra

Alboukadel Kassambara and Fabian Mundt (2017). factoextra: Extract and Visualize the Results of Multivariate Data Analyses. R package version 1.0.5. https://CRAN.R-project.org/package=factoextra

Barret Schloerke, Jason Crowley, Di Cook, Francois Briatte, Moritz Marbach, Edwin Thoen, Amos Elberg and Joseph Larmarange (2018). GGally: Extension to ‘ggplot2’. R package version 1.4.0. https://CRAN.R-project.org/package=GGally

Max Kuhn. Contributions from Jed Wing, Steve Weston, Andre Williams, Chris Keefer, Allan Engelhardt, Tony Cooper, Zachary Mayer, Brenton Kenkel, the R Core Team, Michael Benesty, Reynald Lescarbeau, Andrew Ziem, Luca Scrucca, Yuan Tang, Can Candan and Tyler Hunt. (2019). caret: Classification and Regression Training. R package version 6.0-84. https://CRAN.R-project.org/package=caret

Max Kuhn and Ross Quinlan (2018). C50: C5.0 Decision Trees and Rule-Based Models. R package version 0.1.2. https://CRAN.R-project.org/package=C50

Taiyun Wei and Viliam Simko (2017). R package “corrplot”: Visualization of a Correlation Matrix (Version 0.84). Available from https://github.com/taiyun/corrplot